package xorm

/*
定义表结构体。xorm支持将一个struct（结构体）映射为数据库中对应的一张表
*/

//一 名称映射规则。负责[结构体名称到表名]和[结构体field到表字段的名称]映射
// xorm内置了三种Mapper实现：names.SnakeMapper ， names.SameMapper和names.GonicMapper：
// 	1. SnakeMapper 支持struct（结构体）名称为[驼峰式命名]，表结构为[下划线命名]之间的转换，这个是默认的Mapper；
// 	2. SameMapper 支持struct（结构体）名称和对应的表名称以及结构体field名称与对应的表字段名称相同的命名；
// 	3. GonicMapper 和SnakeMapper很类似，但是对于特定词支持更好，比如ID会翻译成id而不是i_d。
//
// 如果需要改变时，可在engine创建完成后设置。例如设置为SameMapper："engine.SetMapper(names.SameMapper{})"。同时需要注意的是：
// 	1. 如果你使用了别的命名规则映射方案，也可以自己实现一个Mapper；
//  2. 默认表名称和字段名称的映射规则是相同的。当然也可以设置为不同，如：
//		· engine.SetTableMapper(names.SameMapper{})
//		· engine.SetColumnMapper(names.SnakeMapper{})

// 二. 前缀映射，后缀映射和缓存映射
// 	1. 通过 names.NewPrefixMapper(你想要的Mapper, "前缀名字符串") 可以创建一个在Mapper的基础上在命名中添加统一的前缀
// 	   例如，如果希望所有的表名都在结构体自动命名的基础上加一个前缀而字段名不加前缀，则可以在engine创建完成后执行以下语句：
//	func test() *xorm.Engine {
//		if engine, err := xorm.NewMySQL(xorm.MYSQL_DRIVER,
//			"d_mymro_oms_admin:n6sdTiwfj1rqBC3h@tcp(122.51.88.186:3306)/golang_db?charset=utf8"); err != nil{
//			return nil
//		}else{
//			mapper := names.NewPrefixMapper(names.SameMapper{}, "prefix_")
//			engine.SetTableMapper(mapper)
//			return engine
//		}
//	}
// 	   执行之后，结构体 type user struct 默认对应的表名就变成了 prefix_user 了，而之前默认的是 user；
// 	2. 通过 core.NewSufffixMapper(core.SnakeMapper{}, "suffix") 可以创建一个在SnakeMapper的基础上在命名中添加统一的后缀，
//     当然也可以把SnakeMapper换成SameMapper或者你自定义的Mapper；
// 	3. 通过 core.NewCacheMapper(core.SnakeMapper{}) 可以创建一个组合了其它的映射规则，起到在内存中缓存曾经映射过的命名映射

// 三. 使用Table和Tag改变名称映射
// 如果所有的命名都是按照IMapper的映射来操作的，那当然是最理想的。但是如果碰到某个表名或者某个字段名跟映射规则不匹配时，
// 我们就需要别的机制来改变。xorm提供了如下几种方式来进行：
//	1. 如果结构体拥有TableName() string的成员方法，那么此方法的返回值即是该结构体对应的数据库表名；
//	2. 通过engine.Table()方法可以改变struct对应的数据库表的名称，通过sturct中field对应的Tag中使用xorm:"'column_name'"可以
// 	   使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突，因为我们在Tag中还可以
// 	   对这个Column进行更多的定义。如果名称不冲突的情况，单引号也可以不使用
//
// 表名的优先级顺序如下：
//	1. engine.Table() 指定的临时表名优先级最高；
//	2. TableName() string 其次；
//	3. Mapper自动映射的表名优先级最后
// 字段名的优先级顺序如下：
// 1. 结构体tag指定的字段名优先级较高；
// 2. Mapper自动映射的表名优先级较低

// 四. Column属性定义
// 在field对应的Tag中对Column的一些属性进行定义，定义的方法基本和我们写SQL定义表结构类似，比如:
//	type User struct {
//		Id   int64
//		Name string  `xorm:"varchar(25) notnull unique 'usr_name'"`
//	}
//
//对于不同的数据库系统，数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义，基本的原则是尽量兼容各种数据库的字段类型。
//
// 对于使用者，一般只要使用自己熟悉的数据库字段定义即可：https://images.gitee.com/uploads/images/2020/0625/153059_6314cc64_1199468.png
// 另外有如下几条自动映射的规则：
//	1. 如果field名称为Id而且类型为int64并且没有定义tag，则会被xorm视为主键，并且拥有自增属性。如果想用Id以外的名字或非int64类型做为主键名，
//     必须在对应的Tag上加上xorm:"pk"来定义主键，加上xorm:"autoincr"作为自增。这里需要注意的是，有些数据库并不允许非主键的自增属性；
//	2. string类型默认映射为varchar(255)，如果需要不同的定义，可以在tag中自定义，如：varchar(1024)；
//  3. 支持type MyString string等自定义的field，支持Slice, Map等field成员，这些成员默认存储为Text类型，并且默认将使用Json格式来序列化
//     和反序列化。也支持数据库字段类型为Blob类型。如果是Blob类型，则先使用Json格式序列化再转成[]byte格式。如果是[]byte或者[]uint8，
//     则不做转换二十直接以二进制方式存储。具体参见 Go与字段类型对应表；
//  4. 实现了Conversion接口的类型或者结构体，将根据接口的转换方式在类型和数据库记录之间进行相互转换，这个接口的优先级是最高的
//	type Conversion interface {
//		FromDB([]byte) error
//		ToDB() ([]byte, error)
//	}
//  5. 如果一个结构体包含一个Conversion的接口类型，那么在获取数据时，必须要预先设置一个实现此接口的struct或者struct的指针。
//  此时可以在此struct中实现BeforeSet(name string, cell xorm.Cell)方法来进行预先给Conversion赋值
